﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace PlaydeezGames.Common
{
    public interface ICountedCollection<CountedType>
    {
        Dictionary<CountedType, double>.KeyCollection Keys { get; }
        double this[CountedType key] { get; }
    }
    public class CountedCollection<CountedType>:ICountedCollection<CountedType>
    {
        public delegate CountedType FromStringDelegate(string source);
        public delegate string ToStringDelegate(CountedType source);
        private Dictionary<CountedType, double> table = new Dictionary<CountedType,double>();
        public Dictionary<CountedType, double>.KeyCollection Keys
        {
            get
            {
                return table.Keys;
            }
        }
        public double this[CountedType key]
        {
            get
            {
                if (table.ContainsKey(key))
                {
                    return table[key];
                }
                else
                {
                    return 0;
                }
            }
            set
            {
                if (value == 0)
                {
                    if (table.ContainsKey(key))
                    {
                        table.Remove(key);
                    }
                }
                else
                {
                    if (table.ContainsKey(key))
                    {
                        table[key] = value;
                    }
                    else
                    {
                        table.Add(key, value);
                    }
                }
            }
        }
        public void Clear()
        {
            table.Clear();
        }
        public CountedCollection()
        {
        }
        public CountedCollection(CountedCollection<CountedType> copyFrom)
        {
            foreach (CountedType counted in copyFrom.Keys)
            {
                this[counted] = copyFrom[counted];
            }
        }
        public CountedCollection(XElement element,FromStringDelegate fromStringDelegate)
        {
            FromXElement(element, fromStringDelegate);
        }
        private void FromXElement(XElement element, FromStringDelegate fromStringDelegate)
        {
            foreach (XElement subElement in element.Elements(entryXName))
            {
                string valueString = subElement.Element(valueXName).Value;
                string countString = subElement.Element(countXName).Value;
                CountedType value = fromStringDelegate(valueString);
                double count;
                double.TryParse(countString, out count);
                this[value] += count;
            }
        }
        public string DefaultToStringDelegate(CountedType source)
        {
            return source.ToString();
        }
        public XElement ToXElement(XName name)
        {
            return ToXElement(name, DefaultToStringDelegate);
        }
        const string entryXName = "entry";
        const string countXName = "count";
        const string valueXName = "value";
        public XElement ToXElement(XName name,ToStringDelegate toStringDelegate)
        {
            XElement result = new XElement(name);
            foreach (CountedType key in Keys)
            {
                result.Add(new XElement(entryXName,
                    new XElement(valueXName,toStringDelegate(key)),
                    new XElement(countXName,this[key])
                    ));
            }
            return result;
        }
        public static CountedCollection<CountedType> Combine(CountedCollection<CountedType> first, CountedCollection<CountedType> second)
        {
            CountedCollection<CountedType> result = new CountedCollection<CountedType>(first);
            foreach (CountedType key in second.Keys)
            {
                result[key] += second[key];
            }
            return result;
        }
    }
}
